package com.idroc.endpoint.impl;

import java.net.InetSocketAddress;
import java.util.SortedMap;
import java.util.TreeMap;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.idroc.endpoint.Endpoint;
import com.idroc.endpoint.EndpointChangeListner;
import com.idroc.endpoint.Serializer;
import com.idroc.rpc.RPCFactory;
import com.idroc.rpc.RPCProvider;

/**
 * 从端点
 * @author 赤脚詹(yuyoo4j@163.com)
 * @date Aug 17, 2009 4:47:55 PM 
 * @memo:
 */
@SuppressWarnings("serial")
public class SlaveEndpoint implements Endpoint {
	
	private static final Logger log = LoggerFactory.getLogger(SlaveEndpoint.class);
	
	public static final String MASTER_ENDPOINT = "master_endpoint";
	public static final String SLAVE_ENDPOINT_COLLECTION = "slave_endpoint_collection";
	
	private Integer epNo = null;
	private String host = null;
	private Integer port = null;
	
	private transient Serializer serializer = null;
	
	private Endpoint master = null;
	private TreeMap<Integer, Endpoint> slaves = null;
	
	private transient int hashCode = 0;
	
	private transient RPCProvider rpcProvider = null;
	
	private transient EndpointChangeListner listner = null;
	
	public SlaveEndpoint() {}
	
	@SuppressWarnings("unchecked")
	public SlaveEndpoint(Integer epNo, String host, Integer port, Serializer serializer, Endpoint master) {
		
		this.epNo = epNo;
		this.host = host;
		this.port = port;
		this.serializer = serializer;
		this.master = master;
		if (this.master.checkAlive()) {
			slaves = this.master.getSlaveEndpoint();
			serializer.save(MASTER_ENDPOINT, this.master);
			serializer.save(SLAVE_ENDPOINT_COLLECTION, slaves);
			log.info("当前主端点[{}]和从端点集合[{}].", this.master, slaves);
		} else {
			if (null != this.serializer) {
				log.info("尝试从之前保存的端点信息中寻找主端点");
				TreeMap<Integer, Endpoint> old = (TreeMap<Integer, Endpoint>) this.serializer.load(SLAVE_ENDPOINT_COLLECTION);
				if (null != old) {
					log.info("使用之前保存的端点信息寻找主端点. 旧端点:{}", old);
					for (Integer no : old.keySet()) {
						Endpoint oldSlave = old.get(no);
						try {
							Endpoint slave = new ProxyEndpoint();
							if (slave.checkAlive()) {
								Endpoint m = slave.getMasterEndpoint();
								this.master = new ProxyEndpoint(m);
								this.slaves = this.master.getSlaveEndpoint();
								log.info("使用久的端点[{}]寻找主端点成功.", slave);
								log.info("当前主端点[{}]和从端点集合[{}].", this.master, this.slaves);
								break;
							}
						} catch (Exception ex) {
							log.info("使用旧端点[{}]寻找主端点异常", oldSlave);
						}
					}
				} else {
					log.info("未找到之前保存的端点信息");
				}
			}
			String errorMsg = "主端点为启动或者已经崩溃, 从端点不能启动";
			log.error(errorMsg);
			throw new RuntimeException(errorMsg);
		}
		
		// 对外提供rpc服务
		InetSocketAddress endpoint = new InetSocketAddress(host, port);
		rpcProvider = RPCFactory.bindEndpoint(endpoint);
		rpcProvider.exportInterface(Endpoint.class, this);
		
	}

	/*
	 * 
	 * @see com.idroc.endpoint.Endpoint#addEndpoint(com.idroc.endpoint.Endpoint, com.idroc.endpoint.Endpoint)
	 */
	public boolean addEndpoint(Endpoint master, Endpoint newSlave) {

		if (this.master.equals(master) && master.checkAlive()) {
			
			if (this.equals(newSlave)) {
				this.slaves.put(newSlave.getEndpointNo(), this);
			} else {
				Endpoint proxy = new ProxyEndpoint(newSlave);
				this.slaves.put(proxy.getEndpointNo(), proxy);
			}
			boolean saved = serializer.save(SLAVE_ENDPOINT_COLLECTION, slaves);
			log.debug("从端点(SlaveEndpoint)增加端点:{}, newSlave:{}", saved, newSlave); 
			return true;
		}
		return false;
	}

	/*
	 * 
	 * @see com.idroc.endpoint.Endpoint#checkAlive()
	 */
	public boolean checkAlive() { 
		return true;
	}

	/*
	 * 
	 * @see com.idroc.endpoint.Endpoint#dropEndpoint(com.idroc.endpoint.Endpoint, java.lang.Integer)
	 */
	public boolean dropEndpoint(Endpoint master, Integer epNo) {

		if (this.master.equals(master) && master.checkAlive()) {
			Endpoint removed = this.slaves.remove(epNo);
			if (null != removed) {
				this.serializer.save(SLAVE_ENDPOINT_COLLECTION, slaves);
			}
			log.debug("从端点[{}]移除端点epNo:{}", this, epNo);
			return true;
		}
		return false;
	}

	/*
	 * 
	 * @see com.idroc.endpoint.Endpoint#getEndpointHost()
	 */
	public String getEndpointHost() {

		return host;
	}

	/*
	 * 
	 * @see com.idroc.endpoint.Endpoint#getEndpointNo()
	 */
	public Integer getEndpointNo() {

		return epNo;
	}

	/*
	 * 
	 * @see com.idroc.endpoint.Endpoint#getEndpointPort()
	 */
	public Integer getEndpointPort() {

		return port;
	}

	/*
	 * 
	 * @see com.idroc.endpoint.Endpoint#getMasterEndpoint()
	 */
	public Endpoint getMasterEndpoint() {
		
		return this.master;
	}

	/*
	 * 
	 * @see com.idroc.endpoint.Endpoint#getSlaveEndpoint()
	 */
	public TreeMap<Integer, Endpoint> getSlaveEndpoint() {
		 
		return this.slaves;
	}

	/*
	 * 
	 * @see com.idroc.endpoint.Endpoint#isMasterEndpoint()
	 */
	public boolean isMasterEndpoint() {

		return false;
	}

	public Endpoint rebuildMasterEndpoint(boolean nested) {

		if (this.master.checkAlive()) {
			return master; // 如果当前端点是根
		} else if (nested) { // 嵌套查询可行节点并重建
			SortedMap<Integer, Endpoint> tails = slaves.tailMap(Integer.valueOf(master.getEndpointNo().intValue() + 1));
			// 优先取根端点后面的端点然后才是前面的端点作为候选端点集合
			SortedMap<Integer, Endpoint> candicates = (tails.size() > 0) ? tails : slaves.headMap(master.getEndpointNo()); 
			// 遍历端点找一个可以取代当前根结点
			for (Integer no : candicates.keySet()) {		
				try {
					Endpoint next = candicates.get(no);
					Endpoint candicate = next.rebuildMasterEndpoint(false);
					if (null != candicate) {
						return candicate;
					} 
				} catch (Exception ex) {
					ex.printStackTrace();
				}
			}
			return null;
		} else { // 非嵌套查询重建
			SortedMap<Integer, Endpoint> tails = slaves.tailMap(Integer.valueOf(master.getEndpointNo().intValue() + 1));
			// 优先取根端点后面的第一个端点然后才是前面的第一个端点作为候选端点
			Endpoint candicate = (tails.size() > 0) ? tails.get(tails.firstKey()) : slaves.get(slaves.firstKey());
			if (this.equals(candicate) && checkAlive()) {
				 
				// 通知端点更换根端点
				for (Integer no : slaves.keySet()) {
					try { 
						Endpoint elem = slaves.get(no);
						Endpoint newMaster = new MasterEndpoint(this.epNo, this.host, this.port, this.serializer);
						elem.replaceMasterEndpoint(newMaster); 
					} catch (Exception ex) {
						ex.printStackTrace();
					}
				}
				return this;
			}
			return null;
		} 
	}

	/*
	 * 
	 * @see com.idroc.endpoint.Endpoint#registerEndpoint(com.idroc.endpoint.Endpoint)
	 */
	public boolean registerEndpoint(Endpoint ep) {

		return this.master.registerEndpoint(ep);
	}

	/*
	 * 
	 * @see com.idroc.endpoint.Endpoint#replaceMasterEndpoint(com.idroc.endpoint.Endpoint)
	 */
	public boolean replaceMasterEndpoint(Endpoint ep) {

		if (!this.master.checkAlive()) {
			this.master = ep;
			return this.serializer.save(MASTER_ENDPOINT, ep);
		}
		return false;
	}

	/*
	 * 
	 * @see com.idroc.endpoint.Endpoint#unregisterEndpoint(java.lang.Integer)
	 */
	public boolean unregisterEndpoint(Integer epNo) {

		return this.master.unregisterEndpoint(epNo);
	}

	
	/**
	 * 当 编号相同, 主机IP相同, 端口相同, 认为两个端点相同
	 */
	@Override
	public boolean equals(Object obj) { 
		
		if (null == obj) {
			return false;
		}
		if (obj instanceof Endpoint) {
			Endpoint ep = (Endpoint)obj;
			Integer no = ep.getEndpointNo();
			String host = ep.getEndpointHost();
			Integer port = ep.getEndpointPort();
			return epNo.equals(no) && this.host.equals(host) && this.port.equals(port);
		} else {
			return false;
		}
	}

	@Override
	public int hashCode() { 
		
		if (hashCode == 0) {
			
			hashCode = 17;
			hashCode = 37 * hashCode + epNo.hashCode();
			hashCode = 37 * hashCode + host.hashCode();
			hashCode = 37 * hashCode + port.hashCode();
		}
		return hashCode;
	}
	
	@Override
	public String toString() {
		
		StringBuilder sb = new StringBuilder();
		sb.append("SlaveEndpoint[");
		sb.append("epNo:").append(epNo).append(",");
		sb.append("host:").append(host).append(",");
		sb.append("port:").append(port);
		sb.append("]");
		return sb.toString();
	}

	public EndpointChangeListner getListner() {

		return listner;
	}

	public void setListner(EndpointChangeListner listner) {

		this.listner = listner;
	}

}
